package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Component;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.utils.ClassScanAndLoadUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {
    /**
     * 1. 获取包下所有的class文件，加载并将类类型 存入 集合中
     * 2. 通过反射获取class描述
     * a. 执行bean 对象实例化
     * b. 执行依赖属性注入
     * c. 以上bean 已经生成，开始扫描bean方法中是否 @Transactional 标志，如果有则通过代理方式进行包装bean（判断是否实现接口）
     */


    private static Map<String, Object> map = new HashMap<>();  // 存储对象


    static {
        // 1. 获取包下所有的class文件，加载并将类类型 存入 集合中
        Set<Class<?>> clzFromPkg = ClassScanAndLoadUtils.getClzFromPkg("com.lagou.edu");

        // 2. 扫描自定义的bean类型 实例化到bean 容器中
        clzFromPkg.stream().forEach(clazz -> {
            // 如果类使用 Component 注解
            if (!Objects.isNull(clazz.getAnnotation(Component.class))) {
                try {
                    doCreateBean(clazz);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private static void doCreateBean(Class<?> clazz) throws IllegalAccessException, InstantiationException {
        // 1. 实例化 bean对象
        Component annotation = clazz.getAnnotation(Component.class);
        String beanId = StringUtils.isEmpty(annotation.value()) ? clazz.getSimpleName() : annotation.value();
        if (Objects.isNull(map.get(beanId))) {
            Object beanInstance = clazz.newInstance();

            // 2. 属性注入
            Field[] declaredFields = clazz.getDeclaredFields();
            if (declaredFields.length > 0) {
                for (Field field : declaredFields) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    if (!Objects.isNull(autowired)) {
                        Class<?> type = field.getType();
                        String name = field.getName();
                        Object depBeanObject = Objects.isNull(map.get(name)) ? getBeanByType(type) : map.get(name);
                        if (!field.isAccessible()) {
                            field.setAccessible(true);
                        }
                        field.set(beanInstance, depBeanObject);
                    }
                }
            }
            // 3. 检查是否有 Transactional 注解,如果有则构建代理对象返回
            Object wrapperBean = null;
            Method[] declaredMethods = clazz.getDeclaredMethods();
            if (declaredMethods.length > 0) {
                for (Method method : declaredMethods) {
                    Transactional transactional = method.getAnnotation(Transactional.class);
                    if (!Objects.isNull(transactional)) {
                        // 获取事务的代理对象
                        ProxyFactory proxyFactory = (ProxyFactory) (Objects.isNull(map.get("proxyFactory")) ? getBeanByType(ProxyFactory.class) : map.get("proxyFactory"));
                        Class<?>[] interfaces = clazz.getInterfaces();
                        // 如果是实现了接口
                        if (interfaces.length > 0) {
                            wrapperBean = proxyFactory.getJdkProxy(beanInstance);
                        } else {
                            wrapperBean = proxyFactory.getCglibProxy(beanInstance);
                        }
                        map.put(beanId, wrapperBean);
                        break;
                    }
                }
            }
            //
            map.put(beanId, Objects.isNull(wrapperBean)?beanInstance:wrapperBean);
        }
    }

    private static Object getBeanByType(Class<?> type) throws InstantiationException, IllegalAccessException {
        Component annotation = type.getAnnotation(Component.class);
        if (Objects.isNull(annotation)) {
            return null;
        }
        // 尝试创建
        doCreateBean(type);
        String beanId = StringUtils.isEmpty(annotation.value()) ? type.getSimpleName() : annotation.value();
        return map.get(beanId);
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id);
    }

}
